#!/usr/bin/python2
#  Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
#
#  Use of this source code is governed by a BSD-style license
#  that can be found in the LICENSE file in the root of the source
#  tree. An additional intellectual property rights grant can be found
#  in the file PATENTS.  All contributing project authors may
#  be found in the AUTHORS file in the root of the source tree.

#  To run this script please copy "out/<build_name>//pyproto/webrtc/modules/
#  audio_coding/audio_network_adaptor/debug_dump_pb2.py" to this folder.
#  The you can run this script with:
#  "python parse_ana_dump.py -m uplink_bandwidth_bps -f dump_file.dat"
#  You can add as may metrics or decisions to the plot as you like.
#  form more information call:
#  "python parse_ana_dump.py --help"

import struct
from optparse import OptionParser

import matplotlib.pyplot as plt

import debug_dump_pb2


def GetNextMessageSize(file_to_parse):
  data = file_to_parse.read(4)
  if data == '':
    return 0
  return struct.unpack('<I', data)[0]


def GetNextMessageFromFile(file_to_parse):
  message_size = GetNextMessageSize(file_to_parse)
  if message_size == 0:
    return None
  try:
    event = debug_dump_pb2.Event()
    event.ParseFromString(file_to_parse.read(message_size))
  except IOError:
    print 'Invalid message in file'
    return None
  return event


def InitMetrics():
  metrics = {}
  event = debug_dump_pb2.Event()
  for metric in event.network_metrics.DESCRIPTOR.fields:
    metrics[metric.name] = {'time': [], 'value': []}
  return metrics


def InitDecisions():
  decisions = {}
  event = debug_dump_pb2.Event()
  for decision in event.encoder_runtime_config.DESCRIPTOR.fields:
    decisions[decision.name] = {'time': [], 'value': []}
  return decisions


def ParseAnaDump(dump_file_to_parse):
  with open(dump_file_to_parse, 'rb') as file_to_parse:
    metrics = InitMetrics()
    decisions = InitDecisions()
    first_time_stamp = None
    while True:
      event = GetNextMessageFromFile(file_to_parse)
      if event is None:
        break
      if first_time_stamp is None:
        first_time_stamp = event.timestamp
      if event.type == debug_dump_pb2.Event.ENCODER_RUNTIME_CONFIG:
        for decision in event.encoder_runtime_config.DESCRIPTOR.fields:
          if event.encoder_runtime_config.HasField(decision.name):
            decisions[decision.name]['time'].append(event.timestamp -
                                                    first_time_stamp)
            decisions[decision.name]['value'].append(
                getattr(event.encoder_runtime_config, decision.name))
      if event.type == debug_dump_pb2.Event.NETWORK_METRICS:
        for metric in event.network_metrics.DESCRIPTOR.fields:
          if event.network_metrics.HasField(metric.name):
            metrics[metric.name]['time'].append(event.timestamp -
                                                first_time_stamp)
            metrics[metric.name]['value'].append(
                getattr(event.network_metrics, metric.name))
  return (metrics, decisions)


def main():
  parser = OptionParser()
  parser.add_option(
      "-f", "--dump_file", dest="dump_file_to_parse", help="dump file to parse")
  parser.add_option(
      '-m',
      '--metric_plot',
      default=[],
      type=str,
      help='metric key (name of the metric) to plot',
      dest='metric_keys',
      action='append')

  parser.add_option(
      '-d',
      '--decision_plot',
      default=[],
      type=str,
      help='decision key (name of the decision) to plot',
      dest='decision_keys',
      action='append')

  options = parser.parse_args()[0]
  if options.dump_file_to_parse is None:
    print "No dump file to parse is set.\n"
    parser.print_help()
    exit()
  (metrics, decisions) = ParseAnaDump(options.dump_file_to_parse)
  metric_keys = options.metric_keys
  decision_keys = options.decision_keys
  plot_count = len(metric_keys) + len(decision_keys)
  if plot_count == 0:
    print "You have to set at least one metric or decision to plot.\n"
    parser.print_help()
    exit()
  plots = []
  if plot_count == 1:
    f, mp_plot = plt.subplots()
    plots.append(mp_plot)
  else:
    f, mp_plots = plt.subplots(plot_count, sharex=True)
    plots.extend(mp_plots.tolist())

  for key in metric_keys:
    plot = plots.pop()
    plot.grid(True)
    plot.set_title(key + " (metric)")
    plot.plot(metrics[key]['time'], metrics[key]['value'])
  for key in decision_keys:
    plot = plots.pop()
    plot.grid(True)
    plot.set_title(key + " (decision)")
    plot.plot(decisions[key]['time'], decisions[key]['value'])
  f.subplots_adjust(hspace=0.3)
  plt.show()

if __name__ == "__main__":
  main()
